//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package impl

import (
	"context"
	"io"
	"io/fs"
	"os"
	"path/filepath"

	"atomgit.com/openlinksaas-org/ssh-proxy/gen_code"
)

type SftpApiImpl struct{}

func (s *SftpApiImpl) SftpDownloadPost(ctx context.Context, req gen_code.SftpDownloadPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	rf, err := client.sftpClient.Open(req.Path)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer rf.Close()

	lf, err := os.Create(req.LocalPath)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer lf.Close()

	_, err = io.Copy(lf, rf)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: req.LocalPath,
	}, nil
}

func (s *SftpApiImpl) SftpListDirPost(ctx context.Context, req gen_code.SftpListDirPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	fileList, err := client.sftpClient.ReadDir(req.Path)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	fileInfoList := []gen_code.FileInfo{}
	if req.Path != "/" {
		parentPath := Dir(req.Path)
		fileInfoList = append(fileInfoList, gen_code.FileInfo{
			Name: "..",
			Path: parentPath,
			Size: 0,
			Dir:  true,
		})
	}
	for _, file := range fileList {
		size := int64(0)
		if !file.IsDir() {
			size = file.Size()
		}
		path := req.Path + "/" + file.Name()
		if req.Path == "/" {
			path = "/" + file.Name()
		}
		if file.Mode()&fs.ModeSymlink != 0 {
			stat, err := client.sftpClient.Stat(path)
			if err != nil {
				return gen_code.ImplResponse{
					Code: 500,
					Body: &gen_code.ErrInfo{
						ErrMsg: err.Error(),
					},
				}, nil
			}
			if !stat.IsDir() {
				size = stat.Size()
			}
			fileInfoList = append(fileInfoList, gen_code.FileInfo{
				Name: file.Name(),
				Path: path,
				Size: int32(size),
				Dir:  stat.IsDir(),
			})
		} else {
			fileInfoList = append(fileInfoList, gen_code.FileInfo{
				Name: file.Name(),
				Path: path,
				Size: int32(size),
				Dir:  file.IsDir(),
			})
		}
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: &gen_code.SftpListDirPost200Response{
			FileList: fileInfoList,
		},
	}, nil
}

func (s *SftpApiImpl) SftpMkDirPost(ctx context.Context, req gen_code.SftpMkDirPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	path := req.Path + "/" + req.Name
	if req.Path == "/" {
		path = "/" + req.Name
	}
	err := client.sftpClient.Mkdir(path)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: path,
	}, nil
}

func (s *SftpApiImpl) SftpRemovePost(ctx context.Context, req gen_code.SftpListDirPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	err := client.sftpClient.Remove(req.Path)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: req.Path,
	}, nil
}

func (s *SftpApiImpl) SftpRenamePost(ctx context.Context, req gen_code.SftpRenamePostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	err := client.sftpClient.Rename(req.SrcPath, req.DestPath)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: req.DestPath,
	}, nil
}

func (s *SftpApiImpl) SftpUploadPost(ctx context.Context, req gen_code.SftpDownloadPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	lf, err := os.Open(req.LocalPath)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer lf.Close()

	fileName := filepath.Base(req.LocalPath)
	path := req.Path + "/" + fileName
	rf, err := client.sftpClient.Create(path)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer rf.Close()

	_, err = io.Copy(rf, lf)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}

	return gen_code.ImplResponse{
		Code: 200,
		Body: path,
	}, nil
}

func (s *SftpApiImpl) SftpWorkDirPost(ctx context.Context, req gen_code.PortforwardListPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	curPath, err := client.sftpClient.Getwd()
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: curPath,
	}, nil
}

func (s *SftpApiImpl) SftpReadTextPost(ctx context.Context, req gen_code.SftpReadTextPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}
	info, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	client := info.(*ClientInfo)
	if client.sftpClient == nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: "not support sftp",
			},
		}, nil
	}
	rf, err := client.sftpClient.Open(req.Path)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer rf.Close()

	data, err := io.ReadAll(rf)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: &gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: string(data),
	}, nil
}
